package org.zhuzx.util;

/**
 * 异常处理工具类
 * @author zhuzx 2018年11月29日
 */
public class ExceptionUtil {
	
	/**
	 * 获取报错信息和匹配targetPackageName的报错行号，只取第一个匹配行的行号，并在行号前额外显示该类的类名（它是实际报错的类）。
	 * @return 若能获取到有效行号，则将行号以圆括号加在报错信息的后面。
	 */
	public static String getMessage(Exception e, String targetPackageName) {
		String ext = "";
		for (StackTraceElement ste : e.getStackTrace()) {
			if (ste.getClassName().startsWith(targetPackageName) && ste.getLineNumber() > -1) {
				ext = MicroUtil.getSimpleClassName(ste.getClassName()) + ":" + ste.getLineNumber();
				break;
			}
		}
		return e.getMessage() + "（" + ext + "）";
	}
	
	/**
	 * 获取报错信息和匹配targetPackageName的报错行号，以第一个匹配的报错行是否来自targetClass为依据，显示不同行号信息：<br>
	 * 若是，则targetClass就是实际报错的那个类，获取这个类的多个相关行号（调用链），按堆栈打印顺序排列，以英文逗号连接；<br>
	 * 若否，则错误不是targetClass引发的，只取第一个匹配行的行号，并额外显示该类的类名（它是实际报错类且并非targetClass）。
	 * @return 若能获取到有效行号，则将行号以圆括号加在报错信息的后面。
	 */
	public static String getMessage(Exception e, String targetPackageName, Class<?> targetClass) {
		String ext = "";
		boolean classMatched = false;
		for (StackTraceElement ste : e.getStackTrace()) {
			String className = ste.getClassName();
			if (className.startsWith(targetPackageName) && ste.getLineNumber() > -1) {
				if (className.startsWith(targetClass.getName())) {
					classMatched = true;//classMatched变成true之后永远为true，第一次匹配到包路径时若类不匹配则结束循环。
					ext += "," + ste.getLineNumber();
				} else {//若类匹配第一个报错行，则其它那些类不匹配的行都不要，classMatched永远为true，一直循环而不进此分支。
					if (!classMatched) {//第一个匹配包名的报错行，但它不是targetClass抛出的，直接取这一个结束循环，返回。
						//若不是由targetClass直接抛出来的，则额外显示这个类的类名。
						ext += MicroUtil.getSimpleClassName(className) + ":" + ste.getLineNumber();
						break;
					}
				}
			}
		}
		return e.getMessage() + "（" + (classMatched ? ext.substring(1) : ext) + "）";
	}
	
	/**
	 * 获取报错信息和匹配targetClass所在包路径的报错行号，以第一个匹配的报错行是否来自targetClass为依据，显示不同行号信息：<br>
	 * 若是，则targetClass就是实际报错的那个类，获取这个类的多个相关行号（调用链），按堆栈打印顺序排列，以英文逗号连接；<br>
	 * 若否，则错误不是targetClass引发的，只取第一个匹配行的行号，并额外显示该类的类名（它是实际报错类且并非targetClass）。
	 * @return 若能获取到有效行号，则将行号以圆括号加在报错信息的后面。
	 */
	public static String getMessage(Exception e, Class<?> targetClass) {
		return getMessage(e, targetClass.getPackage().getName(), targetClass);
	}

	/**
	 * 描述一个Exception的具体内容，返回与指定类相关的报错信息（通常传入调用者所在的类）。
	 */
	public static String describe(Exception e, Class<?> cla) {
		StringBuilder stackTrace = new StringBuilder();
		String packageName = cla.getPackage().getName();
		for (StackTraceElement ste : e.getStackTrace()) {
			String str = ste.toString();
			if (str.startsWith(packageName)) {
				stackTrace.append(str + "\n");
			}
		}
		if (stackTrace.length() > 0) {
			stackTrace.deleteCharAt(stackTrace.length() - 1);
		}
		return GeneralUtil.concatStringExcludeBlank(": ", e.getClass().getName(), e.getMessage())
				+ "\n" + stackTrace;
	}
	
	/**
	 * 处理异常，printStackTrace并返回携带异常信息的json字符串。
	 * @return 返回{"code":500, "msg":"异常信息"}
	 */
	public static String handle(Exception e) {
		e.printStackTrace();
		String exMsg = e.getMessage();
		exMsg = GeneralUtil.isBlank(exMsg) ? null : exMsg.replace("\"", "'");
		String msg = GeneralUtil.concatStringExcludeBlank(":", e.getClass().getName(), exMsg);
		return "{\"code\":500, \"msg\":\"" + msg + "\"}";
	}
	
	/**
	 * 尝试将异常翻译为直观可读的文本，若无法翻译则返回异常中的getMessage()原文本。
	 */
	public static String translate(Exception e) {
		String msg = e.getMessage();
		if (GeneralUtil.isBlank(msg)) {
			return msg;
		}
		//@Deprecated//待办：根据异常类型进行归类处理。
		return Translator.chainTranslate(msg);
	}
	
	/**
	 * 尝试将异常信息翻译为更直观可读的文本，若无法翻译则返回原文本。
	 */
	public static String translate(String msg) {
		if (GeneralUtil.isBlank(msg)) {
			return msg;
		}
		//@Deprecated//待办：根据文本中可能存在的Exception完整类名，加载异常类型进行归类处理。
		return Translator.chainTranslate(msg);
	}
	
	//@Deprecated//待办：拆分，如sql专用，如网络专用。
	public enum Translator {
		UNIQUE_FIELD_CONFLICT("Duplicate entry", false, "唯一性字段冲突"),
		TOO_LONG("Data too long for column", false, "字段长度超出限制"),
		/* 这种报错的具体原因待定 */
		DATA_TRUNCATED("Data truncated for column", false, "字段长度超出限制或者含有非法字符"),
		INCORRECT_VALUE("Incorrect \\w+ value", true, "字段类型不正确");
		
		private final String keyword;
		private final boolean isRegex;
		private final String description;
		Translator(String keyword, boolean isRegex, String description) {
			this.keyword = keyword;
			this.isRegex = isRegex;
			this.description = description;
		}
		public String getDescription() {
			return description;
		}

		/**
		 * 无法翻译时必须返回null
		 */
		private String translate(String msg) {
			int found = isRegex ? MicroUtil.findFirst(msg, keyword) : msg.indexOf(keyword);
			if (found == -1) {
				return null;
			}
			int start = found + keyword.length();
			int end = MicroUtil.findFirst(msg, "[\n;；.。]", start);
			String line;
			int needed = 2;
			String desc = description;
			while (start < end && needed > 0) {
				line = msg.substring(start, end);
				String hint = MicroUtil.substringByMates(line, '\'', '\'');
				if (hint != null) {
					start = msg.indexOf("'" + hint + "'") + hint.length() + 2;
					desc += "【" + hint + "】";
					needed--;
				} else {
					break;
				}
			}
			return desc;
		}
		
		private static final Translator[] items = values();
		
		/**
		 * 翻译失败则返回原文本
		 */
		static String chainTranslate(String msg) {
			if (GeneralUtil.isBlank(msg)) {
				return msg;
			}
			//若已经含有中文则不需要翻译
			if (MicroUtil.findFirst(msg, UnifiedConstants.REGEX_CHINESE_CHAR) > -1) {
				return msg;
			}
			for (Translator translator : items) {
				String desc = translator.translate(msg);
				if (desc != null) {
					return desc;
				}
			}
			return msg;
		}
	}

	/**
	 * 用于表示一种不可能发生的情况，作为分支的一个声明，明确和强调永远不会进入此分支。<br>
	 * 本该在任何时候都不会触发执行，若实际运行过程中进入了此方法，必定是代码编写有误，既非脏数据也不是外部因素。
	 */
	public static void neverHappen() {
		throw new IllegalStateException("程序逻辑与业务意图相悖，无法继续往下执行。");
	}

	/**
	 * 语义同无参的{@link #neverHappen}
	 */
	public static void neverHappen(String errDetail) {
		throw new IllegalStateException(errDetail + "，无法继续往下执行，需要开发者修正程序逻辑。");
	}

}